{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LjJ7XrjSK4Kp"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "vR6F124oRpTA"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZsYweEXsF1Ut"
      },
      "source": [
        "# Sequential モデル"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zdxri4XdC3IR"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/keras/sequential_model\">     <img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/guide/keras/sequential_model.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"> Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/keras/sequential_model.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/guide/keras/sequential_model.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ygz2642R7AEV"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kPfh5AQ8fFzQ"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "from tensorflow.keras import layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "H86UozfTDwPp"
      },
      "source": [
        "## Sequential モデルの使用が適している場合\n",
        "\n",
        "`Sequential`モデルは、各レイヤーに**{nbsp}1 つの入力テンソルと 1 つの出力テンソルのみ**がある**レイヤーのプレーンスタック**に適しています。\n",
        "\n",
        "概略的には、以下の`Sequential`モデルを参照してください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ajdypoWBxCFi"
      },
      "outputs": [],
      "source": [
        "# Define Sequential model with 3 layers\n",
        "model = keras.Sequential(\n",
        "    [\n",
        "        layers.Dense(2, activation=\"relu\", name=\"layer1\"),\n",
        "        layers.Dense(3, activation=\"relu\", name=\"layer2\"),\n",
        "        layers.Dense(4, name=\"layer3\"),\n",
        "    ]\n",
        ")\n",
        "# Call model on a test input\n",
        "x = tf.ones((3, 3))\n",
        "y = model(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "m9gUL80fmFFo"
      },
      "source": [
        "上記は次の関数と同じです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gqksyI6UN0U3"
      },
      "outputs": [],
      "source": [
        "# Create 3 layers\n",
        "layer1 = layers.Dense(2, activation=\"relu\", name=\"layer1\")\n",
        "layer2 = layers.Dense(3, activation=\"relu\", name=\"layer2\")\n",
        "layer3 = layers.Dense(4, name=\"layer3\")\n",
        "\n",
        "# Call layers on a test input\n",
        "x = tf.ones((3, 3))\n",
        "y = layer3(layer2(layer1(x)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MqyEgCnQVf4w"
      },
      "source": [
        "以下の場合、Sequential モデルは**適切ではありません**。\n",
        "\n",
        "- モデルに複数の入力または複数の出力がある場合\n",
        "- レイヤーに複数の入力または複数の出力がある場合\n",
        "- レイヤーの共有を行う必要がある場合\n",
        "- 非線形トポロジーが必要な場合（残差を用いた接続、複数の分岐点をもつモデルなど）"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sWxFjYzpKzUD"
      },
      "source": [
        "## Sequential モデルの作成\n",
        "\n",
        "レイヤーのリストを Sequential コンストラクタに渡すことにより、Sequential モデルを作成できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "itLg7K6qTzVO"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential(\n",
        "    [\n",
        "        layers.Dense(2, activation=\"relu\"),\n",
        "        layers.Dense(3, activation=\"relu\"),\n",
        "        layers.Dense(4),\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g3fvbcVmCHEP"
      },
      "source": [
        "そのレイヤーには、`layers`属性を介してアクセスできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Lwmuasfp5KXs"
      },
      "outputs": [],
      "source": [
        "model.layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qVNQRoWqJ3PY"
      },
      "source": [
        "また、`add()`メソッドを使用して Sequential モデルを作成することもできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2TD4yRflSEv6"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(layers.Dense(2, activation=\"relu\"))\n",
        "model.add(layers.Dense(3, activation=\"relu\"))\n",
        "model.add(layers.Dense(4))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PmunUrP7HhMI"
      },
      "source": [
        "また、レイヤーを削除するには、 `pop()` メソッドが対応します。Sequential モデルは、レイヤーのリストのように動作します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6INo3oxSLUbA"
      },
      "outputs": [],
      "source": [
        "model.pop()\n",
        "print(len(model.layers))  # 2"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AAvcsRrOxQ9W"
      },
      "source": [
        "また、Sequential コンストラクタは、Keras のレイヤーやモデルと同様に、`name`引数を受け入れます。これは、セマンティックに意味のある命名で TensorBoard グラフに注釈を付けるのに役立ちます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "s8oN1kMYqSuD"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential(name=\"my_sequential\")\n",
        "model.add(layers.Dense(2, activation=\"relu\", name=\"layer1\"))\n",
        "model.add(layers.Dense(3, activation=\"relu\", name=\"layer2\"))\n",
        "model.add(layers.Dense(4, name=\"layer3\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PqGh2rmljliN"
      },
      "source": [
        "## 事前に入力形状を指定\n",
        "\n",
        "一般的に、Keras のすべてのレイヤーは、重みを作成できるようにするために、入力の形状を知る必要があります。このようなレイヤーを作成する場合、当初は重みがありません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tW04KX032Ol9"
      },
      "outputs": [],
      "source": [
        "layer = layers.Dense(3)\n",
        "layer.weights  # Empty"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NU0Ai9lbGUKA"
      },
      "source": [
        "重みの形状は入力の形状に依存するため、入力時に最初に呼び出されたときに重みが作成されます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OqSj1TLCU1vH"
      },
      "outputs": [],
      "source": [
        "# Call layer on a test input\n",
        "x = tf.ones((1, 4))\n",
        "y = layer(x)\n",
        "layer.weights  # Now it has weights, of shape (4, 3) and (3,)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CDF0sLoVrGdU"
      },
      "source": [
        "これは Sequential モデルでも同じです。入力形状なしで Sequential モデルをインスタンス化すると、重みがないために「構築」されません (`model.weights`を呼び出すと、これを示すエラーが発生します)。重みは、モデルが最初に入力データを確認したときに作成されます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ovoPw8Ncbevb"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential(\n",
        "    [\n",
        "        layers.Dense(2, activation=\"relu\"),\n",
        "        layers.Dense(3, activation=\"relu\"),\n",
        "        layers.Dense(4),\n",
        "    ]\n",
        ")  # No weights at this stage!\n",
        "\n",
        "# At this point, you can't do this:\n",
        "# model.weights\n",
        "\n",
        "# You also can't do this:\n",
        "# model.summary()\n",
        "\n",
        "# Call the model on a test input\n",
        "x = tf.ones((1, 4))\n",
        "y = model(x)\n",
        "print(\"Number of weights after calling the model:\", len(model.weights))  # 6"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "laWJparrJg70"
      },
      "source": [
        "モデルが「構築」されたら、その`summary()`メソッドを呼び出して、その内容を表示できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mY2X9QbgabHu"
      },
      "outputs": [],
      "source": [
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1ZDoaIHvwSmw"
      },
      "source": [
        "ただし、Sequential モデルを段階的に構築する場合、その時点の出力形状を含め、それまでのモデルの概要を表示できると非常に便利です。 この場合、モデルに`Input`オブジェクトを渡してモデルを開始し、最初から入力形状がわかるようにする必要があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wv9HeHEeCKAG"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(keras.Input(shape=(4,)))\n",
        "model.add(layers.Dense(2, activation=\"relu\"))\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4rhwhV0mpu6m"
      },
      "source": [
        "`Input`オブジェクトはレイヤーではないため、`model.layers`の一部として表示されないことに注意してください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oCwEwMoy1B0A"
      },
      "outputs": [],
      "source": [
        "model.layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xcKk9VmVY9of"
      },
      "source": [
        "簡単な代替手段として、最初のレイヤーに`input_shape`引数を渡すこともできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FMe4x1CMP75N"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(layers.Dense(2, activation=\"relu\", input_shape=(4,)))\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E8lMCSByqIqP"
      },
      "source": [
        "このような事前定義された入力形状で構築されたモデルは、常に (データが確認される前でも) 重みを持ち、常に定義された出力形状をもっています。\n",
        "\n",
        "一般的に、Sequential モデルの入力形状がわかっている場合は、常に事前に指定しておくことをお勧めします。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W5tJgMQ6phEp"
      },
      "source": [
        "## 一般的なデバッグワークフロー：`add()` + `summary()`\n",
        "\n",
        "新しい Sequential アーキテクチャを構築する場合、`add()`を使用して段階的にレイヤーを積み重ね、モデルの概要を頻繁に出力することをお勧めします。これにより、`Conv2D`レイヤーと`MaxPooling2D`レイヤーのスタックが画像特徴量マップをどのようにダウンサンプリングしているかを監視できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lz8Lo0OOweir"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(keras.Input(shape=(250, 250, 3)))  # 250x250 RGB images\n",
        "model.add(layers.Conv2D(32, 5, strides=2, activation=\"relu\"))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.MaxPooling2D(3))\n",
        "\n",
        "# Can you guess what the current output shape is at this point? Probably not.\n",
        "# Let's just print it:\n",
        "model.summary()\n",
        "\n",
        "# The answer was: (40, 40, 32), so we can keep downsampling...\n",
        "\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.MaxPooling2D(3))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.MaxPooling2D(2))\n",
        "\n",
        "# And now?\n",
        "model.summary()\n",
        "\n",
        "# Now that we have 4x4 feature maps, time to apply global max pooling.\n",
        "model.add(layers.GlobalMaxPooling2D())\n",
        "\n",
        "# Finally, we add a classification layer.\n",
        "model.add(layers.Dense(10))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CNbDnMrNPCWu"
      },
      "source": [
        "これは、非常に実用的なワークフローです。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "doMLAEStLF8l"
      },
      "source": [
        "## モデルの準備ができたら\n",
        "\n",
        "モデルアーキテクチャの準備ができたら、以下を行います。\n",
        "\n",
        "- モデルのトレーニング、評価、推論を実行します。[「組み込みループを使用したトレーニングと評価のガイド」](https://www.tensorflow.org/guide/keras/train_and_evaluate/)をご覧ください。\n",
        "- モデルをディスクに保存して復元します。[「シリアル化と保存のガイド」](https://www.tensorflow.org/guide/keras/save_and_serialize/)をご覧ください。\n",
        "- 複数の GPU を活用してモデルを迅速にトレーニングします。[「マルチ GPU と分散トレーニングのガイド」](distributed_training)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tQ1VzU9LjW6O"
      },
      "source": [
        "## Sequential モデルによる特徴量の抽出\n",
        "\n",
        "Sequential モデルが構築されると、[Functional API モデル](https://www.tensorflow.org/guide/keras/functional/)のように動作します。つまり、すべてのレイヤーに`input`および`output`属性があります。これらの属性を使用すると、Sequential モデルのすべての中間層の出力を抽出するモデルを迅速に作成したりできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DCnNkNL4EEWT"
      },
      "outputs": [],
      "source": [
        "initial_model = keras.Sequential(\n",
        "    [\n",
        "        keras.Input(shape=(250, 250, 3)),\n",
        "        layers.Conv2D(32, 5, strides=2, activation=\"relu\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\"),\n",
        "    ]\n",
        ")\n",
        "feature_extractor = keras.Model(\n",
        "    inputs=initial_model.inputs,\n",
        "    outputs=[layer.output for layer in initial_model.layers],\n",
        ")\n",
        "\n",
        "# Call feature extractor on test input.\n",
        "x = tf.ones((1, 250, 250, 3))\n",
        "features = feature_extractor(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oX0vtFJEr8mC"
      },
      "source": [
        "以下の例では、1 つのレイヤーからのみ特徴量を抽出します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4fCuSyOQFkbq"
      },
      "outputs": [],
      "source": [
        "initial_model = keras.Sequential(\n",
        "    [\n",
        "        keras.Input(shape=(250, 250, 3)),\n",
        "        layers.Conv2D(32, 5, strides=2, activation=\"relu\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\", name=\"my_intermediate_layer\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\"),\n",
        "    ]\n",
        ")\n",
        "feature_extractor = keras.Model(\n",
        "    inputs=initial_model.inputs,\n",
        "    outputs=initial_model.get_layer(name=\"my_intermediate_layer\").output,\n",
        ")\n",
        "# Call feature extractor on test input.\n",
        "x = tf.ones((1, 250, 250, 3))\n",
        "features = feature_extractor(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CdWf9KFXXjM4"
      },
      "source": [
        "## Sequential モデルによる転移学習\n",
        "\n",
        "転移学習では、モデルの最も下のレイヤーをフリーズし、その上にあるレイヤーのみをトレーニングします。転移学習についての詳細は、[「転移学習のガイド」](https://www.tensorflow.org/guide/keras/transfer_learning/)をご覧ください。\n",
        "\n",
        "以下は、Sequential モデルを含む 2 つの一般的な転移学習のブループリントです。\n",
        "\n",
        "まず、Sequential モデルで、最後のレイヤーを除くすべてのレイヤーを凍結するとします。この場合、以下のように単純に`model.layers`で処理を繰り返し、最後のレイヤーを除く各レイヤーで`layer.trainable = False`を設定します。\n",
        "\n",
        "```python\n",
        "model = keras.Sequential([\n",
        "    keras.Input(shape=(784))\n",
        "    layers.Dense(32, activation='relu'),\n",
        "    layers.Dense(32, activation='relu'),\n",
        "    layers.Dense(32, activation='relu'),\n",
        "    layers.Dense(10),\n",
        "])\n",
        "\n",
        "# Presumably you would want to first load pre-trained weights.\n",
        "model.load_weights(...)\n",
        "\n",
        "# Freeze all layers except the last one.\n",
        "for layer in model.layers[:-1]:\n",
        "  layer.trainable = False\n",
        "\n",
        "# Recompile and train (this will only update the weights of the last layer).\n",
        "model.compile(...)\n",
        "model.fit(...)\n",
        "```\n",
        "\n",
        "もう1つの一般的な方法として、Sequential モデルを使用し、事前トレーニング済みのモデルといくつかの新しく初期化された分類レイヤーを以下のようにスタックすることができます。\n",
        "\n",
        "```python\n",
        "# Load a convolutional base with pre-trained weights\n",
        "base_model = keras.applications.Xception(\n",
        "    weights='imagenet',\n",
        "    include_top=False,\n",
        "    pooling='avg')\n",
        "\n",
        "# Freeze the base model\n",
        "base_model.trainable = False\n",
        "\n",
        "# Use a Sequential model to add a trainable classifier on top\n",
        "model = keras.Sequential([\n",
        "    base_model,\n",
        "    layers.Dense(1000),\n",
        "])\n",
        "\n",
        "# Compile & train\n",
        "model.compile(...)\n",
        "model.fit(...)\n",
        "```\n",
        "\n",
        "転移学習を行う場合、これらが頻繁に使用される 2 つのパターンです。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "weB4ei6uNtIg"
      },
      "source": [
        "Sequential モデルについての説明は以上です。\n",
        "\n",
        "Keras でのモデル構築の詳細については、以下を参照してください。\n",
        "\n",
        "- [Functional API のガイド](https://www.tensorflow.org/guide/keras/functional/)\n",
        "- [サブクラス化によるレイヤとモデルの新規作成](https://www.tensorflow.org/guide/keras/custom_layers_and_models/)"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "sequential_model.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
